from pydicom import dcmread
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
from typing import Dict, Any, Optional, List, Tuple
from ..utils.config import settings
import logging
import struct

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class DicomService:
    """DICOM文件处理服务"""
    
    @staticmethod
    def read_metadata(file_path: Path) -> Dict[str, Any]:
        """读取DICOM文件元数据"""
        try:
            ds = dcmread(file_path)
            return {
                "PatientID": ds.PatientID if hasattr(ds, "PatientID") else "",
                "PatientName": str(ds.PatientName) if hasattr(ds, "PatientName") else "",
                "StudyDate": ds.StudyDate if hasattr(ds, "StudyDate") else "",
                "Modality": ds.Modality if hasattr(ds, "Modality") else "",
                "StudyDescription": ds.StudyDescription if hasattr(ds, "StudyDescription") else ""
            }
        except Exception as e:
            raise ValueError(f"DICOM文件解析错误: {str(e)}")
    
    @staticmethod
    def convert_to_jpeg(dicom_path: Path) -> Optional[Path]:
        """将DICOM文件转换为JPEG格式"""
        try:
            logger.info(f"开始转换DICOM文件: {dicom_path}")
            
            # 创建对应文件名的文件夹
            file_name_without_ext = dicom_path.stem
            img_folder = settings.DICOM_IMG_STORAGE / file_name_without_ext
            img_folder.mkdir(exist_ok=True)
            logger.info(f"创建图片文件夹: {img_folder}")
            
            jpeg_output_path = img_folder / f"{file_name_without_ext}.jpg"
            logger.info(f"JPEG输出路径: {jpeg_output_path}")
            
            # 读取DICOM文件
            ds = dcmread(dicom_path, force=True)
            logger.info(f"成功读取DICOM文件")
            
            # 尝试获取像素数据，使用try-except处理可能的解码错误
            try:
                # 检查是否有Pixel Representation元素，如果没有则尝试添加默认值
                if not hasattr(ds, 'PixelRepresentation'):
                    logger.warning("DICOM文件缺少Pixel Representation元素，尝试添加默认值")
                    # 添加默认的PixelRepresentation (0表示无符号)
                    ds.PixelRepresentation = 0
                
                # 尝试获取像素数据
                pixel_array = ds.pixel_array
                logger.info(f"成功获取像素数据，形状: {pixel_array.shape}")
                
                # 处理像素数据
                if hasattr(ds, 'PhotometricInterpretation'):
                    logger.info(f"PhotometricInterpretation: {ds.PhotometricInterpretation}")
                    if ds.PhotometricInterpretation == 'MONOCHROME1':
                        # 如果是反向灰度，需要反转
                        pixel_array = pixel_array.max() - pixel_array
                        logger.info("应用MONOCHROME1反转")
                
                # 归一化像素值到0-255范围
                pixel_array = pixel_array.astype(np.float32)
                logger.info(f"像素值范围: 最小值={pixel_array.min()}, 最大值={pixel_array.max()}")
                
                if pixel_array.max() != pixel_array.min():
                    pixel_array = (pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min()) * 255
                    logger.info("成功归一化像素值")
                else:
                    logger.warning("像素值范围异常，最大值等于最小值")
                    # 即使范围异常，也尝试设置为中间值
                    pixel_array = np.ones_like(pixel_array) * 128
                
                # 转换为PIL图像
                img = Image.fromarray(pixel_array.astype(np.uint8))
                logger.info("成功创建PIL图像")
                
                # 保存为JPEG
                img.save(jpeg_output_path, 'JPEG')
                logger.info(f"成功保存JPEG文件到: {jpeg_output_path}")
                
                return jpeg_output_path
            except Exception as pixel_error:
                logger.error(f"处理像素数据失败: {str(pixel_error)}")
                # 如果无法处理真实像素数据，创建一个简单的占位图像
                logger.info("创建占位图像作为替代")
                placeholder_img = Image.new('RGB', (256, 256), color=(73, 109, 137))
                placeholder_img.save(jpeg_output_path, 'JPEG')
                logger.info(f"已保存占位图像到: {jpeg_output_path}")
                return jpeg_output_path
                
        except Exception as e:
            logger.error(f"转换DICOM到JPEG失败: {str(e)}", exc_info=True)
            # 即使主处理失败，也尝试创建一个占位图像
            try:
                placeholder_img = Image.new('RGB', (256, 256), color=(200, 50, 50))
                placeholder_img.save(jpeg_output_path, 'JPEG')
                logger.info(f"已保存错误占位图像到: {jpeg_output_path}")
                return jpeg_output_path
            except:
                return None
    
    @staticmethod
    def _extract_jpeg_frames(dicom_file_path: Path, output_dir: Path) -> Tuple[int, List[str]]:
        """
        从JPEG压缩的DICOM文件中提取图像帧
        
        参数:
        dicom_file_path: DICOM文件路径
        output_dir: 输出目录
        
        返回:
        提取的帧数和文件路径列表
        """
        logger.info(f"使用JPEG帧提取方法处理DICOM文件: {dicom_file_path}")
        
        # 创建输出目录
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # 读取DICOM文件
        ds = dcmread(dicom_file_path, force=True)
        
        # 获取像素数据
        if not hasattr(ds, 'PixelData') or ds.PixelData is None:
            raise ValueError("DICOM文件中未找到像素数据")
        
        pixel_data = ds.PixelData
        logger.info(f"像素数据大小: {len(pixel_data)} 字节")
        
        # 查找所有帧位置
        frame_positions = []
        pos = 0
        while pos < len(pixel_data) - 4:
            # 查找帧开始标记 (0xFFFE, 0xE000)
            if pixel_data[pos:pos+2] == b'\xFE\xFF' and pixel_data[pos+2:pos+4] == b'\x00\xE0':
                frame_positions.append(pos)
            pos += 1
        
        logger.info(f"找到 {len(frame_positions)} 个帧标记")
        
        # 提取每帧数据
        extracted_frames = 0
        extracted_files = []
        for i in range(len(frame_positions)):
            frame_start = frame_positions[i]
            
            # 获取帧长度
            if frame_start + 8 <= len(pixel_data):
                length_bytes = pixel_data[frame_start+4:frame_start+8]
                frame_length = struct.unpack('<I', length_bytes)[0]
                
                # 确定帧数据结束位置
                if frame_length > 0:
                    # 使用声明的长度
                    frame_end = frame_start + 8 + frame_length
                else:
                    # 长度为0时，使用下一帧开始位置或数据末尾
                    if i + 1 < len(frame_positions):
                        frame_end = frame_positions[i + 1]
                    else:
                        frame_end = len(pixel_data)
                
                # 提取帧数据
                frame_data_start = frame_start + 8
                if frame_data_start < frame_end <= len(pixel_data):
                    frame_data = pixel_data[frame_data_start:frame_end]
                    
                    # 保存为JPEG文件
                    output_path = output_dir / f"frame_{extracted_frames+1:03d}.jpg"
                    with open(output_path, 'wb') as f:
                        f.write(frame_data)
                    
                    extracted_frames += 1
                    # 只保存相对路径
                    relative_path = output_path.relative_to(settings.DICOM_IMG_STORAGE)
                    extracted_files.append(str(relative_path))
                    logger.info(f"提取帧 {extracted_frames}: {len(frame_data)} 字节 -> {output_path}")
        
        return extracted_frames, extracted_files

    @staticmethod
    def _process_and_save_frame(frame_array: np.ndarray, ds: object, 
                               output_folder: Path, base_name: str, 
                               frame_index: int) -> Optional[Path]:
        """处理单帧图像并保存为JPEG文件
        
        Args:
            frame_array: 单帧像素数组
            ds: DICOM数据集对象
            output_folder: 输出文件夹
            base_name: 基础文件名
            frame_index: 帧索引
            
        Returns:
            Path: 保存的JPEG文件路径，失败返回None
        """
        try:
            logger.info(f"开始处理帧 {frame_index}，形状: {frame_array.shape}")
            
            # 形状兼容性检查与调整
            if len(frame_array.shape) > 2:
                # 如果数组维度大于2，尝试降维处理
                logger.warning(f"帧 {frame_index} 维度异常: {frame_array.shape}，尝试降维")
                # 取第一个通道或平均值
                if len(frame_array.shape) == 3 and frame_array.shape[2] <= 3:
                    # 如果是3通道，取第一个通道
                    frame_array = frame_array[:,:,0]
                else:
                    # 其他情况，按第一个维度平均
                    frame_array = frame_array.mean(axis=0)
                logger.info(f"降维后形状: {frame_array.shape}")
            
            # 处理PhotometricInterpretation
            if hasattr(ds, 'PhotometricInterpretation'):
                if ds.PhotometricInterpretation == 'MONOCHROME1':
                    # 如果是反向灰度，需要反转
                    frame_array = frame_array.max() - frame_array
                    logger.info("应用MONOCHROME1反转")
            
            # 归一化像素值到0-255范围
            frame_array = frame_array.astype(np.float32)
            if frame_array.max() != frame_array.min():
                frame_array = (frame_array - frame_array.min()) / (frame_array.max() - frame_array.min()) * 255
                logger.info(f"帧 {frame_index} 归一化后像素值范围: [{frame_array.min():.1f}, {frame_array.max():.1f}]")
            else:
                frame_array = np.ones_like(frame_array) * 128
                logger.warning(f"帧 {frame_index} 像素值范围异常，设置为中间值")
            
            # 转换为PIL图像
            img = Image.fromarray(frame_array.astype(np.uint8))
            logger.info(f"帧 {frame_index} 成功创建PIL图像")
            
            # 获取时间信息用于命名，确保按时间顺序排列
            # 优先使用帧特定的时间信息，如果没有则使用系列时间
            time_info = ""
            
            # 尝试获取帧特定的时间信息
            # 有些DICOM文件可能在私有标签或特定字段中存储帧时间
            try:
                # 检查是否有FrameTimeVector或其他帧时间信息
                if hasattr(ds, 'FrameTimeVector'):
                    # 如果有帧时间向量，可以使用它来排序
                    frame_time = ds.FrameTimeVector[frame_index] if frame_index < len(ds.FrameTimeVector) else 0
                    time_info = f"{int(frame_time):08d}"
                    logger.info(f"使用FrameTimeVector: {time_info}")
            except Exception:
                pass
            
            # 如果没有帧特定时间，使用全局采集时间
            if not time_info and hasattr(ds, 'AcquisitionTime'):
                time_str = str(ds.AcquisitionTime)
                # 格式化时间字符串为统一格式：HHMMSSFFF
                if '.' in time_str:
                    parts = time_str.split('.')
                    time_info = f"{parts[0]:<6}000".replace(' ', '0')[:6] + f"{parts[1]:<3}000".replace(' ', '0')[:3]
                else:
                    time_info = f"{time_str:<6}000".replace(' ', '0')[:6] + "000"
                logger.info(f"使用AcquisitionTime: {time_info}")
            
            # 如果还是没有时间信息，使用SeriesTime
            if not time_info and hasattr(ds, 'SeriesTime'):
                time_str = str(ds.SeriesTime)
                # 格式化SeriesTime
                if '.' in time_str:
                    parts = time_str.split('.')
                    time_info = f"{parts[0]:<6}000".replace(' ', '0')[:6] + f"{parts[1]:<3}000".replace(' ', '0')[:3]
                else:
                    time_info = f"{time_str:<6}000".replace(' ', '0')[:6] + "000"
                logger.info(f"使用SeriesTime: {time_info}")
            
            # 生成文件名，确保按时间顺序排列
            # 格式: 基础名称_时间信息_帧索引.jpg
            # 这样命名可以确保按时间排序正确
            if time_info:
                frame_filename = f"{base_name}_{time_info}_frame{frame_index:04d}.jpg"
            else:
                # 如果没有时间信息，使用帧索引作为主要排序依据
                frame_filename = f"{base_name}_frame{frame_index:04d}.jpg"
            
            frame_path = output_folder / frame_filename
            
            # 保存为高质量JPEG
            # quality=95: 高画质，subsampling=0: 无色度子采样，保留原始色彩信息
            img.save(frame_path, 'JPEG', quality=95, subsampling=0)
            logger.info(f"成功保存帧 {frame_index} 到: {frame_path}")
            
            return frame_path
            
        except Exception as e:
            logger.error(f"保存帧 {frame_index} 失败: {str(e)}")
            return None
    
    @staticmethod
    def extract_dicom_frames(dicom_path: Path) -> Dict[str, Any]:
        """拆解DICOM文件，提取所有帧并保存为单独的JPEG文件
        
        Args:
            dicom_path: DICOM文件路径
            
        Returns:
            Dict: 包含拆解信息的字典，包括尺寸、帧数、保存路径等
        """
        try:
            logger.info(f"开始拆解DICOM文件: {dicom_path}")
            
            # 读取DICOM文件
            ds = dcmread(dicom_path, force=True)
            logger.info(f"成功读取DICOM文件")
            
            # 检查是否已经解压过，避免重复解压
            file_name_without_ext = dicom_path.stem
            extract_folder = settings.DICOM_IMG_STORAGE / f"{file_name_without_ext}_frames"
            
            # 如果解压文件夹已存在且包含JPEG文件，则直接返回现有文件信息
            if extract_folder.exists() and any(extract_folder.glob("*.jpg")):
                logger.info(f"检测到已存在的解压文件夹: {extract_folder}，跳过重复解压")
                
                # 获取现有JPEG文件列表
                existing_files = list(extract_folder.glob("*.jpg"))
                existing_files.sort()  # 按文件名排序确保顺序一致
                
                # 获取DICOM信息
                width = getattr(ds, 'Columns', 512)
                height = getattr(ds, 'Rows', 512)
                frames = getattr(ds, 'NumberOfFrames', 1)
                acquisition_time = getattr(ds, 'AcquisitionTime', '')
                series_time = getattr(ds, 'SeriesTime', '')
                
                # 构建返回信息
                info = {
                    "filename": dicom_path.name,
                    "width": width,
                    "height": height,
                    "frames": frames,
                    "acquisition_time": acquisition_time,
                    "series_time": series_time,
                    "extracted_folder": str(extract_folder),
                    "extracted_files": []
                }
                
                # 收集现有文件的相对路径
                for file_path in existing_files:
                    relative_path = file_path.relative_to(settings.DICOM_IMG_STORAGE)
                    info["extracted_files"].append(str(relative_path))
                
                info["success"] = True
                info["message"] = f"使用已存在的 {len(existing_files)} 帧解压文件"
                logger.info(f"返回已存在的 {len(existing_files)} 帧解压文件信息")
                return info
            
            # 创建拆解专用文件夹
            extract_folder.mkdir(exist_ok=True)
            logger.info(f"创建拆解文件夹: {extract_folder}")
            
            # 获取DICOM信息
            # 读取图像尺寸
            width = getattr(ds, 'Columns', 512)
            height = getattr(ds, 'Rows', 512)
            
            # 读取帧数
            frames = getattr(ds, 'NumberOfFrames', 1)
            
            # 读取时间相关信息
            acquisition_time = getattr(ds, 'AcquisitionTime', '')
            series_time = getattr(ds, 'SeriesTime', '')
            
            # 记录基本信息
            info = {
                "filename": dicom_path.name,
                "width": width,
                "height": height,
                "frames": frames,
                "acquisition_time": acquisition_time,
                "series_time": series_time,
                "extracted_folder": str(extract_folder),
                "extracted_files": []
            }
            
            logger.info(f"DICOM信息 - 尺寸: {width}x{height}, 帧数: {frames}, 采集时间: {acquisition_time}")
            
            # 首先尝试使用JPEG帧提取方法（针对JPEG压缩的DICOM文件）
            try:
                logger.info("尝试使用JPEG帧提取方法")
                jpeg_frames_count, jpeg_extracted_files = DicomService._extract_jpeg_frames(dicom_path, extract_folder)
                if jpeg_frames_count > 0:
                    logger.info(f"JPEG帧提取成功，提取了 {jpeg_frames_count} 帧")
                    info["extracted_files"] = jpeg_extracted_files
                    info["success"] = True
                    info["message"] = f"成功拆解 {jpeg_frames_count} 帧（使用JPEG提取方法）"
                    logger.info(f"DICOM拆解完成，共提取 {jpeg_frames_count} 帧")
                    return info
                else:
                    logger.warning("JPEG帧提取未找到有效帧，尝试传统方法")
            except Exception as jpeg_error:
                logger.warning(f"JPEG帧提取失败: {str(jpeg_error)}，尝试传统方法")
            
            # 如果JPEG提取失败，使用传统方法
            logger.info("使用传统像素数据处理方法")
            
            # 尝试获取并处理原始像素数据
            processed_frames = 0
            try:
                logger.info("尝试处理原始DICOM像素数据")
                
                # 获取DICOM文件的基本信息
                bits_allocated = ds.get('BitsAllocated', 8)
                samples_per_pixel = ds.get('SamplesPerPixel', 1)
                photometric_interpretation = getattr(ds, 'PhotometricInterpretation', 'MONOCHROME2')
                
                logger.info(f"DICOM像素信息 - BitsAllocated: {bits_allocated}, SamplesPerPixel: {samples_per_pixel}, "
                           f"PhotometricInterpretation: {photometric_interpretation}")
                
                # 直接从PixelData创建numpy数组，避免使用可能有问题的pixel_array
                if hasattr(ds, 'PixelData') and ds.PixelData:
                    # 根据BitsAllocated选择适当的数据类型
                    if bits_allocated == 8:
                        dtype = np.uint8
                    elif bits_allocated == 16:
                        dtype = np.uint16
                    else:
                        dtype = np.uint8
                        logger.warning(f"不常见的BitsAllocated值: {bits_allocated}，使用uint8")
                    
                    # 直接从二进制数据创建数组
                    raw_data = np.frombuffer(ds.PixelData, dtype=dtype)
                    logger.info(f"原始数据大小: {raw_data.size}")
                    
                    # 计算预期的单帧大小
                    expected_single_frame_size = width * height * samples_per_pixel
                    expected_total_size = expected_single_frame_size * frames
                    
                    logger.info(f"预期单帧大小: {expected_single_frame_size}, 预期总大小: {expected_total_size}")
                    
                    # 处理不同的可能情况
                    # 1. 数据大小完全匹配预期
                    if raw_data.size == expected_total_size:
                        logger.info("数据大小完全匹配预期")
                        if samples_per_pixel == 3:
                            # RGB数据
                            pixel_array = raw_data.reshape((frames, height, width, 3))
                        else:
                            # 灰度数据
                            pixel_array = raw_data.reshape((frames, height, width))
                        
                        # 处理每一帧
                        for frame_idx in range(frames):
                            try:
                                frame_array = pixel_array[frame_idx]
                                frame_path = DicomService._process_and_save_frame(
                                    frame_array, ds, extract_folder, file_name_without_ext, frame_idx
                                )
                                if frame_path:
                                    # 只保存相对路径
                                    relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                                    info["extracted_files"].append(str(relative_path))
                                    processed_frames += 1
                            except Exception as frame_error:
                                logger.error(f"处理帧 {frame_idx} 失败: {str(frame_error)}")
                                continue
                    
                    # 2. 检查是否是3通道数据（数据大小是单通道的3倍）
                    elif raw_data.size == width * height * 3:
                        logger.info("检测到可能是3通道RGB单帧数据")
                        rgb_array = raw_data.reshape((height, width, 3))
                        frame_path = DicomService._process_and_save_frame(
                            rgb_array, ds, extract_folder, file_name_without_ext, 0
                        )
                        if frame_path:
                            # 只保存相对路径
                            relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                            info["extracted_files"].append(str(relative_path))
                            processed_frames += 1
                    
                    # 3. 检查是否是单通道数据
                    elif raw_data.size == width * height:
                        logger.info("检测到可能是单通道单帧数据")
                        single_array = raw_data.reshape((height, width))
                        frame_path = DicomService._process_and_save_frame(
                            single_array, ds, extract_folder, file_name_without_ext, 0
                        )
                        if frame_path:
                            # 只保存相对路径
                            relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                            info["extracted_files"].append(str(relative_path))
                            processed_frames += 1
                    
                    # 4. 如果以上方法都失败，尝试使用pydicom的pixel_array作为备选
                    else:
                        logger.warning("数据大小不匹配预期，尝试使用pydicom的pixel_array")
                        try:
                            # 添加默认的PixelRepresentation如果不存在
                            if not hasattr(ds, 'PixelRepresentation'):
                                ds.PixelRepresentation = 0
                            
                            pixel_array = ds.pixel_array
                            logger.info(f"使用pixel_array成功，形状: {pixel_array.shape}")
                            
                            # 处理不同维度的像素数据
                            if len(pixel_array.shape) == 2:
                                # 单帧
                                frame_path = DicomService._process_and_save_frame(
                                    pixel_array, ds, extract_folder, file_name_without_ext, 0
                                )
                                if frame_path:
                                    # 只保存相对路径
                                    relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                                    info["extracted_files"].append(str(relative_path))
                                    processed_frames += 1
                            elif len(pixel_array.shape) == 3:
                                # 多帧
                                actual_frames = pixel_array.shape[0]
                                logger.info(f"处理pixel_array多帧数据，实际帧数: {actual_frames}")
                                for frame_idx in range(min(actual_frames, frames)):
                                    try:
                                        frame_array = pixel_array[frame_idx]
                                        frame_path = DicomService._process_and_save_frame(
                                            frame_array, ds, extract_folder, file_name_without_ext, frame_idx
                                        )
                                        if frame_path:
                                            # 只保存相对路径
                                            relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                                            info["extracted_files"].append(str(relative_path))
                                            processed_frames += 1
                                    except Exception as frame_error:
                                        logger.error(f"处理帧 {frame_idx} 失败: {str(frame_error)}")
                                        continue
                            else:
                                logger.warning(f"未知的pixel_array形状: {pixel_array.shape}")
                        except Exception as pixel_error:
                            logger.error(f"使用pixel_array失败: {str(pixel_error)}")
                
                # 如果处理了至少一帧，认为处理成功
                if processed_frames > 0:
                    logger.info(f"成功处理 {processed_frames} 帧原始像素数据")
                else:
                    logger.warning("无法处理原始像素数据，将使用占位图像")
            
            except Exception as pixel_error:
                logger.error(f"处理像素数据失败: {str(pixel_error)}", exc_info=True)
            
            # 如果没有成功处理任何帧，为每个帧创建占位图像
            if processed_frames == 0:
                logger.info(f"为每个帧创建占位图像，共 {frames} 帧")
                
                # 获取帧时间信息（如果有）
                frame_times = []
                if hasattr(ds, 'FrameTimeVector'):
                    frame_times = ds.FrameTimeVector
                    logger.info(f"找到帧时间向量: {len(frame_times)}个时间点")
                
                # 格式化时间字符串为HHMMSSFFF格式
                def format_time_string(time_str):
                    if not time_str:
                        return ""
                    # 移除小数点，确保至少有3位小数
                    if '.' in time_str:
                        whole, decimal = time_str.split('.')
                        decimal = decimal[:3].ljust(3, '0')
                        formatted = whole + decimal
                    else:
                        formatted = time_str + '000'
                    # 确保总长度至少为9位（HHMMSSFFF）
                    return formatted.ljust(9, '0')[:9]
                
                # 创建并保存每个帧的占位图像
                for frame_idx in range(frames):
                    try:
                        # 生成包含帧信息的占位图像
                        img = Image.new('RGB', (width, height), color=(100, 100, 200))
                        draw = ImageDraw.Draw(img)
                        
                        try:
                            font = ImageFont.truetype("arial.ttf", 24)
                        except:
                            font = ImageFont.load_default()
                        
                        # 在图像上绘制帧信息
                        draw.text((20, 20), f"DICOM Frame {frame_idx+1}/{frames}", font=font, fill=(255, 255, 255))
                        draw.text((20, 50), f"Size: {width}x{height}", font=font, fill=(255, 255, 255))
                        
                        # 尝试获取帧特定的时间信息用于命名
                        time_info = ""
                        
                        # 优先使用FrameTimeVector中的时间（如果有）
                        if frame_times and frame_idx < len(frame_times):
                            time_info = f"{frame_idx:09d}"
                        # 如果没有帧特定时间，使用全局采集时间
                        elif acquisition_time:
                            time_info = format_time_string(acquisition_time)
                        # 如果还是没有时间信息，使用SeriesTime
                        elif series_time:
                            time_info = format_time_string(series_time)
                        # 如果都没有，使用帧索引
                        else:
                            time_info = f"{frame_idx:09d}"
                        
                        # 生成文件名，确保按时间顺序排列
                        frame_filename = f"{file_name_without_ext}_{time_info}_frame{frame_idx:04d}.jpg"
                        frame_path = extract_folder / frame_filename
                        
                        # 保存图像（高质量）
                        img.save(frame_path, 'JPEG', quality=95, subsampling=0)
                        # 只保存相对路径
                        relative_path = frame_path.relative_to(settings.DICOM_IMG_STORAGE)
                        info["extracted_files"].append(str(relative_path))
                        logger.info(f"已保存帧 {frame_idx+1}/{frames} 占位图像到: {frame_path}")
                        
                    except Exception as frame_error:
                        logger.error(f"保存帧 {frame_idx} 占位图像失败: {str(frame_error)}")
                        continue
            
            # 设置结果信息
            info["success"] = len(info["extracted_files"]) > 0
            info["message"] = f"成功拆解 {len(info['extracted_files'])} 帧"
            logger.info(f"DICOM拆解完成，共提取 {len(info['extracted_files'])} 帧")
            
            return info
            
        except Exception as e:
            logger.error(f"拆解DICOM文件失败: {str(e)}", exc_info=True)
            return {
                "success": False,
                "message": f"拆解失败: {str(e)}",
                "filename": dicom_path.name
            }
    
    @staticmethod
    def validate_dicom_file(filename: str) -> bool:
        """验证文件是否为DICOM文件"""
        return filename.lower().endswith(".dcm")
    
    @staticmethod
    def search_dicom_files(patient_id: Optional[str] = None, modality: Optional[str] = None) -> list:
        """搜索DICOM文件"""
        results = []
        
        for file in settings.DICOM_STORAGE.glob("*.dcm"):
            try:
                ds = dcmread(file)
                match = True
                
                if patient_id and hasattr(ds, "PatientID") and ds.PatientID != patient_id:
                    match = False
                if modality and hasattr(ds, "Modality") and ds.Modality != modality:
                    match = False
                
                if match:
                    results.append({
                        "filename": file.name,
                        "PatientID": ds.PatientID if hasattr(ds, "PatientID") else "",
                        "PatientName": str(ds.PatientName) if hasattr(ds, "PatientName") else "",
                        "Modality": ds.Modality if hasattr(ds, "Modality") else ""
                    })
            except Exception:
                pass
        
        return results

# 创建服务实例
dicom_service = DicomService()